先用一個例子開頭,大家猜猜最後value會是多少?
package main
import (
"fmt"
"sync"
)
var x = 0
func increment(wg *sync.WaitGroup) {
x = x + 1
wg.Done()
}
func main() {
var w sync.WaitGroup
for i := 0; i < 1000; i++ {
w.Add(1)
go increment(&w)
}
w.Wait()
fmt.Println("final value of x", x)
}
看起來是1000,但在IDE上執行會發現幾乎都是9XX
為什麼會造成這種情況呢?
答案是Race condiction
如圖所示,當兩個goroutine同時去操縱x的時候,導致x只被加了一次,因此結果會小於正確的 1000。
為了避免race condiction,我們可以使用sync.Mutex上鎖,官網上說:This concept is called mutual exclusion, and the conventional name for the data structure that provides it is mutex.
維基百科介紹:
互斥鎖(英語:Mutual exclusion,縮寫Mutex)是一種用於多執行緒編程中,防止兩條執行緒同時對同一公共資源(比如全域變數)進行讀寫的機制。
所以使用mutex可以避免race condiction,我們把程式修改如下:
package main
import (
"fmt"
"sync"
)
var x = 0
func increment(wg *sync.WaitGroup, m *sync.Mutex) {
m.Lock()
x = x + 1
m.Unlock()
wg.Done()
}
func main() {
var w sync.WaitGroup
var m sync.Mutex
for i := 0; i < 1000; i++ {
w.Add(1)
go increment(&w, &m)
}
w.Wait()
fmt.Println("final value of x", x)
}
Go還有一個特別的地方就是可以利用Channel傳值,如果說 goroutine 是 Go語言程式的併發體的話,那麼 channels 就是它們之間的通信機制。一個 channels 是一個通信機制,它可以讓一個 goroutine 通過它給另一個 goroutine 發送值信息。每個 channel 都有一個型別,也就是 channels 可發送數據的類型。一個可以發送 int 類型數據的 channel 一般寫為 chan int。
package main
import (
"fmt"
)
func hello(done chan bool) {
fmt.Println("Hello world goroutine")
done <- true
}
func main() {
done := make(chan bool)
go hello(done)
<-done
fmt.Println("main function")
}
// Hello world goroutine
main function
可以看到透過channel的方式回傳了Hello world goroutine
channel也可以搭配range跟close來使用
package main
import "fmt"
func main() {
percentageChannel := make(chan int)
go updatePercentage(percentageChannel)
for i := range percentageChannel {
fmt.Println(i)
// 當i == 100時關閉channel
if i == 100 {
close(percentageChannel)
break
}
}
}
func updatePercentage(channel chan int) {
for i := 1; i <= 100; i++ {
channel <- i
}
}